#!/usr/bin/env python3
# -*- coding: utf-8 -*-

import serial
import serial.tools.list_ports
import threading
import time
from datetime import datetime
from typing import Callable, Optional, List, Dict, Any

from ..server.data_parse import is_modbus_data, parse_modbus_rtu


class SerialManager:
    """串口通信管理类"""
    
    def __init__(self, message_callback: Optional[Callable] = None):
        """
        初始化串口管理器
        
        Args:
            message_callback: 消息回调函数，用于向GUI发送日志消息
                             函数签名: callback(message: str, level: str = "INFO", msg_type: str = "normal")
                             msg_type可以是: "normal", "send", "receive", "error"
        """
        self.serial_connection = None
        self.running = False
        self.refresh_timer = None
        
        # 回调函数
        self.message_callback = message_callback or self._default_callback
        
    def _default_callback(self, message: str, level: str = "INFO", msg_type: str = "normal"):
        """默认回调函数，打印到控制台"""
        timestamp = datetime.now().strftime("%H:%M:%S.%f")[:-3]
        print(f"[{timestamp}] {level}: {message}")
    
    def get_available_ports(self) -> List[str]:
        """获取可用的串口列表"""
        try:
            return [port.device for port in serial.tools.list_ports.comports()]
        except Exception as e:
            self.message_callback(f"获取串口列表错误: {e}", "ERROR", "error")
            return []
    
    def connect_serial(self, port: str, baudrate: int = 9600, databits: int = 8, 
                      parity: str = 'N', stopbits: int = 1) -> bool:
        """
        连接串口
        
        Args:
            port: 串口名称
            baudrate: 波特率
            databits: 数据位
            parity: 校验位 ('N', 'E', 'O')
            stopbits: 停止位
            
        Returns:
            bool: 连接是否成功
        """
        try:
            if self.serial_connection and self.serial_connection.is_open:
                self.serial_connection.close()
            
            if not port:
                self.message_callback("请选择串口", "ERROR", "error")
                return False
            
            self.serial_connection = serial.Serial(
                port=port,
                baudrate=baudrate,
                bytesize=databits,
                parity=parity,
                stopbits=stopbits,
                timeout=1
            )
            
            self.running = True
            self.message_callback(f"串口已打开: {port}, {baudrate}, {databits}{parity}{stopbits}")
            
            # 启动接收线程
            threading.Thread(target=self._receive_thread, daemon=True).start()
            
            return True
            
        except Exception as e:
            self.message_callback(f"打开串口失败: {e}", "ERROR", "error")
            return False
    
    def disconnect_serial(self):
        """断开串口连接"""
        try:
            self.running = False
            
            if self.serial_connection and self.serial_connection.is_open:
                self.serial_connection.close()
            
            self.message_callback("串口已关闭")
            
        except Exception as e:
            self.message_callback(f"关闭串口错误: {e}", "ERROR", "error")
    
    def _receive_thread(self):
        """串口接收线程"""
        while self.running and self.serial_connection and self.serial_connection.is_open:
            try:
                if self.serial_connection.in_waiting > 0:
                    data = self.serial_connection.read(self.serial_connection.in_waiting)
                    if data:
                        self.message_callback(f"📨 接收: {data.hex().upper()}", "INFO", "receive")
                        
                        # 解析Modbus数据
                        if is_modbus_data(data):
                            parsed = parse_modbus_rtu(data)
                            self._log_modbus_data(parsed)
                        else:
                            self.message_callback("📄 非Modbus数据", "INFO", "receive")
                
                time.sleep(0.1)
                
            except Exception as e:
                if self.running:
                    self.message_callback(f"串口接收错误: {e}", "ERROR", "error")
                break
    
    def _log_modbus_data(self, parsed_result: Dict[str, Any]):
        """记录Modbus解析结果"""
        self.message_callback("┌─ Modbus解析结果 ─────────────────────", "INFO", "receive")
        
        if "error" in parsed_result:
            self.message_callback(f"│ ❌ 解析错误: {parsed_result['error']}", "INFO", "receive")
        else:
            self.message_callback(f"│ 📍 设备地址: {parsed_result.get('slave_id', 'N/A')}", "INFO", "receive")
            self.message_callback(f"│ 🔧 功能码: {parsed_result.get('function_code', 'N/A')} - {parsed_result.get('function', 'N/A')}", "INFO", "receive")
            self.message_callback(f"│ ✅ CRC校验: {'通过' if parsed_result.get('crc_valid', False) else '失败'}", "INFO", "receive")
            
            # 显示寄存器数据
            if "registers" in parsed_result:
                self.message_callback(f"│ 📊 寄存器数量: {len(parsed_result['registers'])}", "INFO", "receive")
                for i, reg_val in enumerate(parsed_result['registers']):
                    hex_val = parsed_result.get('register_hex', [''])[i] if i < len(parsed_result.get('register_hex', [])) else f"0x{reg_val:04X}"
                    self.message_callback(f"│    寄存器[{i}]: {reg_val} ({hex_val})", "INFO", "receive")
            
            # 显示线圈数据
            if "coil_data" in parsed_result:
                self.message_callback(f"│ 🔘 线圈数据: {parsed_result['coil_data']}", "INFO", "receive")
        
        self.message_callback("└─────────────────────────────────────", "INFO", "receive")
    
    def send_rtu_command(self, cmd_str: str, auto_crc: bool = True) -> bool:
        """
        发送RTU指令
        
        Args:
            cmd_str: RTU指令字符串（十六进制格式）
            auto_crc: 是否自动计算CRC
            
        Returns:
            bool: 发送是否成功
        """
        if not self.serial_connection or not self.serial_connection.is_open:
            self.message_callback("请先打开串口", "ERROR", "error")
            return False
        
        try:
            if not cmd_str.strip():
                self.message_callback("请输入RTU指令", "ERROR", "error")
                return False
            
            # 解析十六进制字符串
            hex_str = cmd_str.replace(' ', '').replace('-', '').replace(':', '')
            if len(hex_str) % 2 != 0:
                hex_str = '0' + hex_str
            
            data = bytes.fromhex(hex_str)
            
            # 检查是否需要自动计算CRC
            if auto_crc:
                if not self._has_crc(data):
                    # 没有CRC，自动计算并添加
                    crc = self._calculate_crc(data)
                    data = data + crc
                    self.message_callback(f"🔧 自动添加CRC: {crc.hex().upper()}")
                else:
                    # 已有CRC，验证是否正确
                    if self._has_crc(data):
                        self.message_callback("✅ CRC校验: 正确")
                    else:
                        self.message_callback("⚠️ CRC校验: 错误，但仍按原样发送")
            
            # 发送数据
            self.serial_connection.write(data)
            self.message_callback(f"📤 发送: {data.hex().upper()}", "INFO", "send")
            
            # 验证是否为Modbus数据
            if is_modbus_data(data):
                parsed = parse_modbus_rtu(data)
                function_name = parsed.get('function', '未知功能')
                self.message_callback(f"🔧 指令类型: {function_name}", "INFO", "send")
            
            return True
            
        except Exception as e:
            self.message_callback(f"发送RTU指令失败: {e}", "ERROR", "error")
            return False
    
    def calculate_crc_for_command(self, cmd_str: str) -> Optional[str]:
        """
        为指令计算CRC
        
        Args:
            cmd_str: RTU指令字符串（十六进制格式）
            
        Returns:
            str: CRC值的十六进制字符串，如果失败返回None
        """
        try:
            if not cmd_str.strip():
                return None
            
            # 解析十六进制字符串
            hex_str = cmd_str.replace(' ', '').replace('-', '').replace(':', '')
            if len(hex_str) % 2 != 0:
                hex_str = '0' + hex_str
            
            data = bytes.fromhex(hex_str)
            
            # 检查是否已经包含CRC
            if self._has_crc(data) and len(data) >= 4:
                # 移除现有的CRC，重新计算
                data_without_crc = data[:-2]
                self.message_callback("检测到现有CRC，将重新计算")
            else:
                data_without_crc = data
            
            # 计算CRC
            crc = self._calculate_crc(data_without_crc)
            crc_hex = crc.hex().upper()
            
            # 构建完整的指令
            complete_cmd = data_without_crc.hex().upper() + crc_hex
            formatted_cmd = ' '.join([complete_cmd[i:i+2] for i in range(0, len(complete_cmd), 2)])
            
            self.message_callback(f"🔧 CRC计算结果:")
            self.message_callback(f"   原始数据: {data_without_crc.hex().upper()}")
            self.message_callback(f"   CRC值: {crc_hex}")
            self.message_callback(f"   完整指令: {formatted_cmd}")
            
            return crc_hex
            
        except Exception as e:
            self.message_callback(f"计算CRC失败: {e}", "ERROR", "error")
            return None
    
    def _calculate_crc(self, data: bytes) -> bytes:
        """计算Modbus RTU CRC校验码"""
        crc = 0xFFFF
        for pos in data:
            crc ^= pos
            for _ in range(8):
                if (crc & 1) != 0:
                    crc >>= 1
                    crc ^= 0xA001
                else:
                    crc >>= 1
        return crc.to_bytes(2, 'little')
    
    def _has_crc(self, data: bytes) -> bool:
        """检查数据是否已包含CRC"""
        if len(data) < 4:  # Modbus RTU最少需要4字节（地址+功能码+CRC）
            return False
        
        # 计算不包含最后两字节的CRC
        data_without_crc = data[:-2]
        calculated_crc = self._calculate_crc(data_without_crc)
        received_crc = data[-2:]
        
        return calculated_crc == received_crc
    
    def start_port_refresh_timer(self, interval: float = 3.0):
        """启动串口实时刷新定时器"""
        def refresh_timer():
            if self.running:
                # 这里可以添加端口变化检测逻辑
                self.refresh_timer = threading.Timer(interval, refresh_timer)
                self.refresh_timer.daemon = True
                self.refresh_timer.start()
        
        refresh_timer()
    
    def stop_port_refresh_timer(self):
        """停止串口刷新定时器"""
        if self.refresh_timer:
            self.refresh_timer.cancel()
    
    def is_connected(self) -> bool:
        """检查串口是否已连接"""
        return self.serial_connection is not None and self.serial_connection.is_open
